﻿
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using AmiEmuLib.AmigaConfiguration;
using AmiEmuLib.AmigaMachine;
using AmiEmuLib.SaveState;
using AmiEmuLib.AmigaMachine.AmigaCPU;
using AmiEmuLib.AmigaConfiguration.CoreConfig;
using AmiEmuLib.AmigaMachine.AmigaMemory;
using AmiEmuLib;
using System.Globalization;
using AmiEmuLib.Configuration;
using System.Xml;
using AmiEmuLib.Configuration.KnownRomConfig;
using AmiEmuLib.Configuration.DefaultAmigaConfigsConfig;
using TomanuExtensions;

namespace amiemu_lib_test
{
    [TestClass()]
    public class SaveStateTest : TestBase
    {
        private string m_temp_file;

        public TestContext TestContext { get; set; }

        [TestInitialize]
        public void Init()
        {
            m_temp_file = Path.GetTempFileName();
        }

        [TestCleanup]
        public void Cleanup()
        {
            if (new FileInfo(m_temp_file).Exists)
                new FileInfo(m_temp_file).Delete();
        }

        [TestCategory("SaveState"), TestCategory("CPU"), TestMethod]
        public void SaveState_CPU_68000_Test()
        {
            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(
                AmigaModel.A500).First().GetConfigCopy();

            config.Memory.ChipMemorySize = 0;
            config.Memory.Kickstart.RomType = RomType.Undefined;
            config.Memory.SlowMemorySize = 0;

            Machine machine = new Machine(config);

            Assert.IsTrue(machine.Memory.GetValidBanks().Count() == 0);

            // Null diff state.
            State initial_state = new State(machine);
            State diff_state = new State(machine, initial_state);
            Assert.IsTrue(diff_state.CPUState.NothingChanged);
            Assert.IsTrue(diff_state.CPUState.Prev != null);
            Assert.IsTrue(Object.ReferenceEquals(diff_state.Prev, initial_state));
            Assert.IsTrue(diff_state.CPUState.Equals(diff_state.CPUState.Prev));
            Assert.IsTrue(Object.ReferenceEquals(diff_state.CPUState.Prev,
                diff_state.Prev.CPUState));

            CPU68000 cpu = (CPU68000)machine.CPU;

            CPU_Value_Test(machine, cpu, 
                (c) => c.A0, (c, value) => c.A0 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A1, (c, value) => c.A1 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A2, (c, value) => c.A2 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A3, (c, value) => c.A3 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A4, (c, value) => c.A4 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A5, (c, value) => c.A5 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A6, (c, value) => c.A6 = value, 0xFFFFFFFF);

            CPU_Value_Test(machine, cpu, 
                (c) => c.D0, (c, value) => c.D0 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D1, (c, value) => c.D1 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D2, (c, value) => c.D2 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D3, (c, value) => c.D3 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D4, (c, value) => c.D4 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D5, (c, value) => c.D5 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D6, (c, value) => c.D6 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D7, (c, value) => c.D7 = value, 0xFFFFFFFF);

            CPU_Value_Test(machine, cpu, 
                (c) => c.CycleCounter, (c, value) => c.CycleCounter = value, 
                0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.IR, (c, value) => c.IR = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.IRC, (c, value) => c.IRC = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.PC, (c, value) => c.PC = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.SP, (c, value) => c.SP = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.SR, (c, value) => c.SR = value, (ushort)0xFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.SSP, (c, value) => c.SSP = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.USP, (c, value) => c.USP = value, 0xFFFFFFFF);
        }

        [TestCategory("SaveState"), TestCategory("CPU"), TestMethod]
        public void SaveState_CPU_68020_Test()
        {
            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(
                AmigaModel.A1200).First().GetConfigCopy();

            config.Memory.ChipMemorySize = 0;
            config.Memory.Kickstart.RomType = RomType.Undefined;

            Machine machine = new Machine(config);

            Assert.IsTrue(machine.Memory.GetValidBanks().Count() == 0);

            // Null diff state.
            State initial_state = new State(machine);
            State diff_state = new State(machine, initial_state);
            Assert.IsTrue(diff_state.CPUState.NothingChanged);
            Assert.IsTrue(diff_state.CPUState.Prev != null);
            Assert.IsTrue(Object.ReferenceEquals(diff_state.Prev, initial_state));
            Assert.IsTrue(diff_state.CPUState.Equals(diff_state.CPUState.Prev));
            Assert.IsTrue(Object.ReferenceEquals(diff_state.CPUState.Prev,
                diff_state.Prev.CPUState));

            CPU68020 cpu = (CPU68020)machine.CPU;

            CPU_Value_Test(machine, cpu, 
                (c) => c.A0, (c, value) => c.A0 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A1, (c, value) => c.A1 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A2, (c, value) => c.A2 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A3, (c, value) => c.A3 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A4, (c, value) => c.A4 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A5, (c, value) => c.A5 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.A6, (c, value) => c.A6 = value, 0xFFFFFFFF);

            CPU_Value_Test(machine, cpu, 
                (c) => c.D0, (c, value) => c.D0 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D1, (c, value) => c.D1 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D2, (c, value) => c.D2 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D3, (c, value) => c.D3 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D4, (c, value) => c.D4 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D5, (c, value) => c.D5 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D6, (c, value) => c.D6 = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.D7, (c, value) => c.D7 = value, 0xFFFFFFFF);

            CPU_Value_Test(machine, cpu, 
                (c) => c.CycleCounter, (c, value) => c.CycleCounter = value,  0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.IR, (c, value) => c.IR = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.IRC, (c, value) => c.IRC = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.PC, (c, value) => c.PC = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.SP, (c, value) => c.SP = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.SR, (c, value) => c.SR = value, (ushort)0xFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.CAAR, (c, value) => c.CAAR = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.CACR, (c, value) => c.CACR = value, (uint)0x00000001);
            CPU_Value_Test(machine, cpu, 
                (c) => c.DFC, (c, value) => c.DFC = value, (byte)0x01);
            CPU_Value_Test(machine, cpu, 
                (c) => c.SFC, (c, value) => c.SFC = value, (byte)0x01);
            CPU_Value_Test(machine, cpu, 
                (c) => c.ISP, (c, value) => c.ISP = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.MSP, (c, value) => c.MSP = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.USP, (c, value) => c.USP = value, 0xFFFFFFFF);
            CPU_Value_Test(machine, cpu, 
                (c) => c.VBR, (c, value) => c.VBR = value, 0xFFFFFFFF);
        }

        private void CPU_Value_Test<T, C>(Machine a_machine, C a_cpu, 
            Func<C, T> a_get, Action<C, T> a_set, T a_new_value) where C : CPU680x0
        {
            State initial_state = new State(a_machine);
            T inital_value = a_get((C)a_machine.CPU);

            a_set((C)a_machine.CPU, a_new_value);
            State after_change_state1 = new State(a_machine);
            Assert.AreNotEqual(initial_state, after_change_state1);

            Machine after_change_machine1 = new Machine(after_change_state1);
            State after_change_state2 = new State(after_change_machine1);
            Assert.AreEqual(after_change_state1, after_change_state2);
            Assert.AreNotEqual(initial_state, after_change_state2);

            a_set((C)after_change_machine1.CPU, inital_value);
            State after_change_state3 = new State(after_change_machine1);
            Assert.AreEqual(initial_state, after_change_state3);
            Assert.AreNotEqual(after_change_state2, after_change_state3);
            Assert.AreNotEqual(after_change_state1, after_change_state3);

            a_set((C)a_machine.CPU, inital_value);

            CPU_Value_Diff(a_machine, a_cpu, a_get, a_set, a_new_value);
        }

        private void CPU_Value_Diff<T, C>(Machine a_machine, C a_cpu,
            Func<C, T> a_get, Action<C, T> a_set, T a_new_value) where C : CPU680x0
        {
            T inital_value = a_get((C)a_machine.CPU);


            State root_state = new State(a_machine);

            Assert.IsTrue(root_state.CPUState.Prev == null);

            State diff_state1 = new State(a_machine, root_state);

            Assert.IsTrue(diff_state1.CPUState.Prev != null);
            Assert.IsTrue(Object.ReferenceEquals(diff_state1.Prev, root_state));
            Assert.IsTrue(diff_state1.CPUState.Equals(diff_state1.CPUState.Prev));
            Assert.IsTrue(Object.ReferenceEquals(diff_state1.CPUState.Prev,
                diff_state1.Prev.CPUState));

            MemoryStream ms1 = new MemoryStream();
            diff_state1.Save(ms1);
            ms1.Position = 0;
            State diff_state2 = State.LoadFromXml(ms1, root_state);
            Assert.AreEqual(diff_state1, diff_state2);

            Assert.IsTrue(diff_state2.CPUState.Prev != null);
            Assert.IsTrue(Object.ReferenceEquals(diff_state2.Prev, root_state));
            Assert.IsTrue(diff_state2.CPUState.Equals(diff_state2.CPUState.Prev));
            Assert.IsTrue(Object.ReferenceEquals(diff_state2.CPUState.Prev,
                diff_state2.Prev.CPUState));

            ms1.Position = 0;
            
            XmlDocument xml = new XmlDocument();
            xml.Load(ms1);
            Assert.IsTrue(xml.SelectNodes("State/*").Count == 0);
            
            a_set((C)a_machine.CPU, a_new_value);

            State diff_state3 = new State(a_machine, root_state);

            Assert.IsTrue(diff_state3.CPUState.Prev != null);
            Assert.IsTrue(Object.ReferenceEquals(diff_state3.Prev, root_state));
            Assert.IsTrue(!diff_state3.CPUState.Equals(diff_state3.CPUState.Prev));
            Assert.IsTrue(Object.ReferenceEquals(diff_state3.CPUState.Prev, 
                diff_state3.Prev.CPUState));

            MemoryStream ms2 = new MemoryStream();
            diff_state3.Save(ms2);
            ms2.Position = 0;
            State diff_state4 = State.LoadFromXml(ms2, root_state);
            Assert.AreEqual(diff_state3, diff_state4);
            Assert.AreNotEqual(diff_state3, diff_state1);

            ms2.Position = 0;
            xml.Load(ms2);
            Assert.IsTrue(xml.SelectNodes("State/*").Count == 1);
            if (a_cpu is CPU68000)
                Assert.IsTrue(xml.SelectNodes("State/CPU68000State/*").Count == 1);
            else
                Assert.IsTrue(xml.SelectNodes("State/CPU68020State/*").Count == 1);

            Machine machine1 = new Machine(diff_state2);
            Machine machine2 = new Machine(diff_state4);
            State state1 = new State(machine1);
            State state2 = new State(machine2);

            Assert.AreEqual(root_state, state1);
            Assert.AreNotEqual(root_state, state2);
            Assert.AreNotEqual(state1, state2);

            a_set((C)a_machine.CPU, inital_value);
        }

        [TestCategory("SaveState"), TestMethod]
        public void SaveState_XmlCmp_Test()
        {
            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(
                    AmigaModel.CD32).First().GetConfigCopy();
            Machine machine = new Machine(config);


            State.Counter = 0;
            State state1 = new State(machine);

            machine.CPU.A0 = 0x55;
            machine.Memory.FillWords(100, 3000, 0x4567);

            State state2 = new State(machine, state1);

            MemoryStream ms = new MemoryStream();
            state1.Save(ms);
            byte[] b1 = ms.ToArray();

            ms = new MemoryStream();
            state2.Save(ms);
            byte[] b2 = ms.ToArray();

            byte[] b1a = null;
            using (var stream = StreamExtensions.FromResource(typeof(SaveStateTest), 
                "root_state_cd32.xml", "xmls"))
            {
                b1a = stream.ReadAll();
            }

            byte[] b2a = null;
            using (var stream = StreamExtensions.FromResource(typeof(SaveStateTest), 
                "diff_state_cd32.xml", "xmls"))
            {
                b2a = stream.ReadAll();
            }

            state1.Save("root_state_cd32.xml");
            state2.Save("diff_state_cd32.xml");

            Assert.IsTrue(b1.AreSame(b1a));
            Assert.IsTrue(b2.AreSame(b2a));  
        }

        [TestCategory("SaveState"), TestMethod]
        public void SaveState_Test()
        {
            foreach (var def_config in DefaultAmigaConfigs.Configs)
                SaveState_Test(def_config.GetConfigCopy());

            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(
                AmigaModel.A500).First().GetConfigCopy();
            config.Memory.SlowMemorySize = 0;
            config.Memory.ChipMemorySize = 0;
            config.Memory.Kickstart.RomType = RomType.Undefined;
            SaveState_Test(config);
        }

        public void SaveState_Test(AmigaConfig config)
        {
            string temp = Path.GetTempFileName();

            Machine machine = null;

            try
            {
                machine = new Machine(config);
            }
            catch (RomLoadException)
            {
                RomLoadExceptionValid(config);
                return;
            }
            catch (CPUInitializationException)
            {
                CPUIniitalizationExceptionValid(config);
                return;
            }

            try
            {
                State state1 = new State(machine);
                Machine machine1 = new Machine(state1);

                State state2 = new State(machine1);
                Assert.AreEqual(state1, state2);

                state2.Save(temp);
                State state3 = State.LoadFromXml(temp);
                Assert.AreEqual(state2, state2);
            }
            finally
            {
                new FileInfo(temp).Delete();
            }
        }

        [TestCategory("SaveState"), TestCategory("AmigaConfiguration"), TestMethod]
        public void SaveState_Config_Test()
        {
            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(
                AmigaModel.A500).First().GetConfigCopy();

            Machine machine = new Machine(config);

            State initial_state = new State(machine);

            Assert.IsTrue(initial_state.Config.Equals(config));

            State root_state = new State(machine);

            Assert.IsTrue(root_state.Config.Equals(config));

            State diff_state = new State(machine, root_state);
            Assert.IsTrue(diff_state.Config == null);
        }

        [TestCategory("SaveState"), TestMethod]
        // TODO: dokladny test roznicowych xmli, skupic sie zwlasza na skomplikowanym xmlu, 
        //       ktory ladujemy i po ktorym poruszamy sie dowolnie, w przod i w tyl, 
        //       Looup publiczne
        // TODO: wstepne indeksowanie pliku, cachowanie. Dwie klasy jedna sluzy do zapisu 
        // savestatow.
        // Druga do jego czytania w sposob dowolny albo szeregowy. Czytanie dowolne pierwszy 
        // odczyt dokonuje 
        // indeksowania.
        public void SaveState_StatesManager_Test()
        {
            //Assert.Fail();
        }

        [TestCategory("Memory"), TestCategory("SaveState"), TestMethod]
        public void SaveState_Memory_Test()
        {
            // Create machine. 

            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(
                AmigaModel.A500).First().GetConfigCopy();

            config.Memory.ChipMemorySize = "0.5MB";
            config.Memory.SlowMemorySize = "1MB";
            config.Memory.Kickstart.RomType = RomType.Undefined;

            Machine machine1 = new Machine(config);

            // Init.

            MersenneTwister random = new MersenneTwister();

            MemoryBank bank1 = machine1.Memory.GetBank(0x00000000);
            for (uint addr = bank1.Address; addr <= bank1.LastWordAddress; addr += 2)
                machine1.Memory.SetWord(addr, random.NextUShort());

            MemoryBank bank2 = machine1.Memory.GetBank(0x00C00000);
            for (uint addr = bank2.Address; addr <= bank2.LastWordAddress; addr += 2)
                machine1.Memory.SetWord(addr, random.NextUShort());

            MemoryBank[] banks = new[] { bank1, bank2 };

            State initial_state = new State(machine1);

            // Null diff state.

            State diff_state1 = new State(machine1, initial_state);
            Assert.IsTrue(diff_state1.MemoryState.Banks.Count() == 0);
            Assert.IsTrue(diff_state1.MemoryState.NothingChanged);
            Assert.IsTrue(diff_state1.MemoryState.Prev != null);
            Assert.IsTrue(Object.ReferenceEquals(diff_state1.Prev, initial_state));
            Assert.IsTrue(Object.ReferenceEquals(diff_state1.MemoryState.Prev,
                diff_state1.Prev.MemoryState));
            
            // Simple test.

            machine1.Memory.FillWords(bank1.Address, 0x1000, 0x2222);
            machine1.Memory.FillWords(0x5000, 0x1000, 0x4444);
            machine1.Memory.FillWords(0x6000, 0x1000, 0x8888);
            machine1.Memory.FillWords(0xB000, 0x1000, 0x1111);
            machine1.Memory.FillWords(bank1.LastWordAddress - 0x1000 + 2, 0x1000, 0x7777);

            State state = new State(machine1);

            Assert.IsTrue(state.MemoryState[0].Ranges.Count() == 8);

            Assert.IsTrue(state.MemoryState[0][0].Address == 0);
            Assert.IsTrue(state.MemoryState[0][0].Size == 0x1000);
            Assert.IsTrue((state.MemoryState[0][0] as FillMemoryRange).Pattern == 0x2222);

            Assert.IsTrue(state.MemoryState[0][1].Address == 0x1000);
            Assert.IsTrue(state.MemoryState[0][1].Size == 0x4000);
            Assert.IsTrue(state.MemoryState[0][1] is AreaMemoryRange);

            Assert.IsTrue(state.MemoryState[0][2].Address == 0x5000);
            Assert.IsTrue(state.MemoryState[0][2].Size == 0x1000);
            Assert.IsTrue((state.MemoryState[0][2] as FillMemoryRange).Pattern == 0x4444);

            Assert.IsTrue(state.MemoryState[0][3].Address == 0x6000);
            Assert.IsTrue(state.MemoryState[0][3].Size == 0x1000);
            Assert.IsTrue((state.MemoryState[0][3] as FillMemoryRange).Pattern == 0x8888);

            Assert.IsTrue(state.MemoryState[0][4].Address == 0x7000);
            Assert.IsTrue(state.MemoryState[0][4].Size == 16 * 1024);
            Assert.IsTrue(state.MemoryState[0][4] is AreaMemoryRange);

            Assert.IsTrue(state.MemoryState[0][5].Address == 0xB000);
            Assert.IsTrue(state.MemoryState[0][5].Size == 0x1000);
            Assert.IsTrue((state.MemoryState[0][5] as FillMemoryRange).Pattern == 0x1111);

            Assert.IsTrue(state.MemoryState[0][6].Address == 0xC000);
            Assert.IsTrue(state.MemoryState[0][6].Size == 12 * 1024);
            Assert.IsTrue(state.MemoryState[0][6] is AreaMemoryRange);

            Assert.IsTrue(state.MemoryState[0][7].Address == 
                bank1.LastWordAddress - 0x1000 + 2);
            Assert.IsTrue(state.MemoryState[0][7].Size == 0x1000);
            Assert.IsTrue((state.MemoryState[0][7] as FillMemoryRange).Pattern == 0x7777);

            // Simple range merging.

            initial_state.Restore(machine1);
            machine1.Memory.FillWords(0, 20, 0x5555);
            machine1.Memory.FillWords(3000, 20, 0x5555);
            state = new State(machine1);
            Assert.IsTrue(state.MemoryState[0].Address == 0);
            Assert.IsTrue(state.MemoryState[0].Ranges.Count() == 2);
            Assert.IsTrue(state.MemoryState[0][0].Size == 20);
            Assert.IsTrue(state.MemoryState[0][1].Size == Consts.BANK_SIZE - 20);

            // Random test.

            Machine machine2 = new Machine(initial_state);

            ProgressIndicator pi = new ProgressIndicator();
            pi.TestName = "SaveState_Memory_Test";

            for (int i = 0; i < 140; i+=10)
            {
                pi.AddLine(String .Format("{0} / {1}", (i/10)+1, 140/10));

                initial_state.Restore(machine1);

                for (int bank_index = 0; bank_index < banks.Length; bank_index++)
                {
                    MemoryBank bank = banks[bank_index];

                    // At begin.
                    if (random.Next(10) > 8)
                    {
                        machine1.Memory.FillWords(0,
                            random.NextUInt(500) & Consts.WORD_MASK, random.NextUShort(10));
                    }

                    // At end.
                    if (random.Next(10) > 8)
                    {
                        uint size = random.NextUInt(500) & Consts.WORD_MASK;
                        machine1.Memory.FillWords(bank.LastWordAddress - size + 2,
                            size, random.NextUShort(10));
                    }

                    // Anywhere.
                    for (int j = 0; j < i; j++)
                    {
                        uint address = random.NextUInt(bank.Address, 
                            bank.LastWordAddress) & Consts.WORD_MASK;
                        uint size = random.NextUInt(500) & Consts.WORD_MASK;

                        if (address + size > bank.LastWordAddress + 2)
                            size = bank.LastWordAddress - address + 2;

                        machine1.Memory.FillWords(address, size, random.NextUShort(10));
                    }
                }

                // Test full state.

                State full_state = new State(machine1);
                full_state.Restore(machine2);

                Assert.IsTrue(full_state.CPUState != null);
                Assert.IsTrue(full_state.Config != null);

                Assert_SameBanks(machine1, machine2, bank1.Address);
                Assert_SameBanks(machine1, machine2, bank2.Address);

                initial_state.Restore(machine2);
                full_state.Save(m_temp_file);
                State full_state_loaded = State.LoadFromXml(m_temp_file);
                Assert.AreEqual(full_state, full_state_loaded);
                full_state_loaded.Restore(machine2);

                Assert_SameBanks(machine1, machine2, bank1.Address);
                Assert_SameBanks(machine1, machine2, bank2.Address);

                MemoryStream ms = new MemoryStream();
                full_state_loaded.Save(ms);
                ms.Position = 0;
                XmlDocument xml = new XmlDocument();
                xml.Load(ms);
                Assert.IsTrue(xml.SelectNodes("State/*").Count != 0);
                Assert.IsTrue(xml.SelectSingleNode("State/AmigaConfig") != null);
                Assert.IsTrue(xml.SelectSingleNode("State/MemoryState") != null);
                Assert.IsTrue(xml.SelectNodes("State/MemoryState/*").Count != 0);
                Assert.IsTrue(xml.SelectSingleNode("State/CPU68000State") != null);

                // Test diff state.

                State diff_state = new State(machine1, initial_state);

                Assert.IsTrue(Object.ReferenceEquals(diff_state.Prev, initial_state));
                Assert.IsTrue(Object.ReferenceEquals(diff_state.MemoryState.Prev,
                    diff_state.Prev.MemoryState));

                diff_state.Restore(machine2);

                Assert.IsTrue(diff_state.CPUState.NothingChanged);
                Assert.IsTrue(diff_state.Config == null);

                Assert_SameBanks(machine1, machine2, bank1.Address);
                Assert_SameBanks(machine1, machine2, bank2.Address);

                initial_state.Restore(machine2);
                ms = new MemoryStream();
                diff_state.Save(ms);
                ms.Position = 0;
                State diff_state_loaded = State.LoadFromXml(ms, initial_state);
                Assert.AreEqual(diff_state, diff_state_loaded);
                diff_state_loaded.Restore(machine2);

                Assert.IsTrue(Object.ReferenceEquals(diff_state_loaded.Prev, initial_state));
                Assert.IsTrue(Object.ReferenceEquals(diff_state_loaded.MemoryState.Prev,
                    diff_state_loaded.Prev.MemoryState));

                Assert_SameBanks(machine1, machine2, bank1.Address);
                Assert_SameBanks(machine1, machine2, bank2.Address);

                ms = new MemoryStream();
                diff_state_loaded.Save(ms);
                ms.Position = 0;
                xml = new XmlDocument();
                xml.Load(ms);
                if (diff_state_loaded.MemoryState.Banks.Count() == 0)
                    Assert.IsTrue(xml.SelectNodes("State/*").Count == 0);
                else
                {
                    Assert.IsTrue(xml.SelectNodes("State/*").Count == 1);
                    Assert.IsTrue(xml.SelectSingleNode("State/MemoryState") != null);
                    Assert.IsTrue(xml.SelectNodes("State/MemoryState/*").Count != 0);
                }

                Assert.IsTrue(xml.SelectSingleNode("State/Config") == null);
                Assert.IsTrue(xml.SelectSingleNode("State/CPU68000State") == null);
            }

            pi.Close();
        }
    }
}
